package org.byqh.dqeai.common;

import com.easy.query.api.proxy.entity.delete.ExpressionDeletable;
import com.easy.query.api.proxy.entity.insert.EntityInsertable;
import com.easy.query.api.proxy.entity.select.EntityQueryable;
import com.easy.query.api.proxy.entity.update.EntityUpdatable;
import com.easy.query.core.api.pagination.EasyPageResult;
import com.easy.query.core.basic.api.delete.ClientExpressionDeletable;
import com.easy.query.core.basic.api.insert.ClientInsertable;
import com.easy.query.core.basic.api.select.ClientQueryable;
import com.easy.query.core.basic.api.update.ClientEntityUpdatable;
import com.easy.query.core.proxy.AbstractProxyEntity;
import com.easy.query.core.proxy.ProxyEntityAvailable;
import com.easy.query.core.util.EasyObjectUtil;
import io.swagger.v3.oas.annotations.Operation;
import org.byqh.dqeai.config.datasource.DataSource;
import org.byqh.dqeai.config.datasource.DataSourceNames;
import org.byqh.dqeai.config.easyquery.EasyQueryClass;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.stream.Collectors;

@DataSource(DataSourceNames.Default)
public abstract class EasyQueryController<
        TEntity extends ProxyEntityAvailable<TEntity, TProxy>,
        TProxy extends AbstractProxyEntity<TProxy, TEntity>
        > extends EasyQueryClass {

    protected Class<TEntity> getGenericClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<TEntity>) parameterizedType.getActualTypeArguments()[0];
    }

    /**
     * 添加。
     *
     * @param entity
     * @return {@code true} 添加成功，{@code false} 添加失败
     */
    @PostMapping("saveOne")
    @Operation(summary = "添加数据")
    public Json<Boolean> save(@RequestBody TEntity entity) {
        try {
            long rows = getEntityInsertable(entity).executeRows();

            return rows > 0 ? Json.success() : Json.error();
        } catch (Exception e) {
            return Json.exception(e);
        }
    }

    /**
     * 添加。
     *
     * @param list
     * @return {@code true} 添加成功，{@code false} 添加失败
     */
    @PostMapping("saveBatch")
    @Operation(summary = "批量添加数据")
    public Json<Boolean> saveBatch(@RequestBody List<TEntity> list) {
        try {
            long rows = getEntityInsertable(list).executeRows();

            return rows > 0 ? Json.success() : Json.error();
        } catch (Exception e) {
            return Json.exception(e);
        }
    }

    /**
     * 根据主键删除。
     *
     * @param id 主键
     * @return {@code true} 删除成功，{@code false} 删除失败
     */
    @RequestMapping(
            value = "removeOneById",
            method = {RequestMethod.GET, RequestMethod.DELETE}
    )
    @Operation(summary = "根据主键删除数据")
    public Json<Boolean> removeById(@RequestParam String id) {
        try {
            long rows = getEntityDeletable()
                    .whereById(id)
                    .executeRows();

            return rows > 0 ? Json.success() : Json.error();
        } catch (Exception e) {
            return Json.exception(e);
        }
    }

    /**
     * 根据主键批量删除数据。
     *
     * @return {@code true} 删除成功，{@code false} 删除失败
     */
    @RequestMapping(
            value = "removeBatchByIds",
            method = {RequestMethod.GET, RequestMethod.DELETE}
    )
    @Operation(summary = "根据主键批量删除数据")
    public Json<Boolean> removeBatchByIds(
            @RequestParam(value = "ids[]", required = false) List<String> ids1,
            @RequestParam(value = "ids", required = false) List<String> ids2
    ) {
        try {
            List<String> ids = Optional.ofNullable(ids1).orElse(ids2);

            long rows = getEntityDeletable()
                    .whereByIds(ids)
                    .executeRows();

            return rows > 0 ? Json.success() : Json.error();
        } catch (Exception e) {
            return Json.exception(e);
        }
    }

    /**
     * 根据主键更新。
     *
     * @param entity
     * @return {@code true} 更新成功，{@code false} 更新失败
     */
    @RequestMapping(
            value = "updateOne",
            method = {RequestMethod.PUT, RequestMethod.POST}
    )
    @Operation(summary = "根据主键更新")
    public Json<Boolean> update(@RequestBody TEntity entity) {
        try {
            long rows = getEntityUpdatable(entity).executeRows();

            return rows > 0 ? Json.success() : Json.error();
        } catch (Exception e) {
            return Json.exception(e);
        }
    }

    /**
     * 根据主键更新。
     *
     * @param list
     * @return {@code true} 更新成功，{@code false} 更新失败
     */
    @RequestMapping(
            value = "updateBatch",
            method = {RequestMethod.PUT, RequestMethod.POST}
    )
    @Operation(summary = "根据主键批量更新")
    public Json<Boolean> updateBatch(@RequestBody List<TEntity> list) {
        try {
            long rows = getEntityUpdatable(list).executeRows();

            return rows > 0 ? Json.success() : Json.error();
        } catch (Exception e) {
            return Json.exception(e);
        }
    }

    /**
     * 查询所有数据。
     *
     * @return 所有数据
     */
    @GetMapping("listEach")
    @Operation(summary = "查询所有数据")
    public Json<List<TEntity>> listEach() {
        try {
            List<TEntity> list = getQueryable().toList();

            return Json.success(list);
        } catch (Exception e) {
            return Json.exceptionT(e);
        }
    }

    /**
     * 根据主键获取列表。
     *
     * @return 列表
     */
    @GetMapping("listByIds")
    @Operation(summary = "根据主键获取列表")
    public Json<List<TEntity>> listByIds(
            @RequestParam(value = "ids[]", required = false) List<String> ids1,
            @RequestParam(value = "ids", required = false) List<String> ids2
    ) {
        try {
            List<String> ids = Optional.ofNullable(ids1).orElse(ids2);

            List<TEntity> list = getEntityQueryable()
                    .whereByIds(ids)
                    .toList();

            return Json.success(list);
        } catch (Exception e) {
            return Json.exceptionT(e);
        }
    }

    /**
     * 根据主键获取详细信息。
     *
     * @param id 主键
     * @return 详情
     */
    @GetMapping("getOneById")
    @Operation(summary = "根据主键获取详细信息")
    public Json<TEntity> getById(@RequestParam String id) {
        try {
            TEntity tEntity = getQueryable()
                    .whereById(id)
                    .firstOrNull();

            return Json.success(tEntity);
        } catch (Exception e) {
            return Json.exceptionT(e);
        }
    }

    /*
     * 获取列去重数据。
     */
    @GetMapping("getDistinctDataByProperty")
    @Operation(summary = "获取列去重数据")
    public Json<List<Object>> getDistinctDataByProperty(@RequestParam String property) {
        try {
            Class<Map<String, Object>> mapClass = EasyObjectUtil.typeCastNullable(Map.class);

            List<Map<String, Object>> listMap = getQueryable()
                    .select(property)
                    .distinct()
                    .select(mapClass)
                    .toList();

            List<Object> listData = listMap.stream()
                    .filter(o -> o.containsKey(property))
                    .map(o -> o.get(property))
                    .collect(Collectors.toList());

            return Json.success(listData);
        } catch (Exception e) {
            return Json.exceptionT(e);
        }
    }

    /**
     * 根据主键获取详细信息。
     *
     * @param mapPK 主键
     * @return 详情
     */
    @GetMapping("getOneByPK")
    @Operation(summary = "根据主键获取详细信息")
    public Json<TEntity> getByPK(@RequestParam Map<String, String> mapPK) {
        try {
            Map<String, String> filterMap = filterMap(mapPK);

            ClientQueryable<TEntity> queryable = getQueryable();

            for (var entry : filterMap.entrySet()) {
                queryable = queryable.where(d -> d.eq(entry.getKey(), entry.getValue()));
            }

            return Json.success(queryable.firstOrNull());
        } catch (Exception e) {
            return Json.exceptionT(e);
        }
    }

    /**
     * 根据主键删除数据。
     *
     * @param mapPK 主键
     * @return {@code true} 删除成功，{@code false} 删除失败
     */
    @GetMapping("removeOneByPK")
    @Operation(summary = "根据主键删除数据")
    public Json<Boolean> removeByPK(@RequestParam Map<String, String> mapPK) {
        try {
            Map<String, String> filterMap = filterMap(mapPK);

            if (filterMap.isEmpty())
                return Json.error();

            ClientExpressionDeletable<TEntity> deletable = getDeletable();

            for (var entry : filterMap.entrySet()) {
                deletable = deletable.where(d -> d.eq(entry.getKey(), entry.getValue()));
            }

            long rows = deletable.executeRows();

            return rows > 0 ? Json.success() : Json.error();
        } catch (Exception e) {
            return Json.exception(e);
        }
    }

    /**
     * 分页查询数据。
     *
     * @param page 分页对象
     * @return 分页对象
     */
    @GetMapping("pageList")
    @Operation(summary = "分页查询数据")
    public Json<EasyPageResult<TEntity>> page(@RequestParam Integer page, @RequestParam Integer size) {
        try {
            EasyPageResult<TEntity> pageResult = getEntityQueryable().toPageResult(page, size);

            return Json.success(pageResult);
        } catch (Exception e) {
            return Json.exceptionT(e);
        }
    }

    /**
     * 条件查询数据
     */
    @GetMapping("listByCondition")
    @Operation(summary = "条件查询数据")
    public Json<List<TEntity>> listByCondition(@RequestParam Map<String, String> condition) {
        try {
            Map<String, String> filterMap = filterMap(condition);

            ClientQueryable<TEntity> queryable = queryOrderBy(getQueryableByCondition(filterMap));

            List<TEntity> list = queryable.toList();

            return Json.success(list);
        } catch (Exception e) {
            return Json.exceptionT(e);
        }
    }

    /**
     * 分页条件查询数据
     */
    @GetMapping("pageByCondition")
    @Operation(summary = "分页条件查询数据")
    public Json<EasyPageResult<TEntity>> pageByCondition(@RequestParam Integer page, @RequestParam Integer size, @RequestParam Map<String, String> condition) {
        try {
            Map<String, String> filterMap = filterMap(condition, "page", "size");

            ClientQueryable<TEntity> queryable = queryOrderBy(getQueryableByCondition(filterMap));

            EasyPageResult<TEntity> pageResult = queryable.toPageResult(page, size);

            return Json.success(pageResult);
        } catch (Exception e) {
            return Json.exceptionT(e);
        }
    }

    public ClientInsertable<TEntity> getInsertable(TEntity entity) {
        return easyQueryClient.insertable(entity);
    }

    public ClientInsertable<TEntity> getInsertable(List<TEntity> list) {
        return easyQueryClient.insertable(list);
    }

    public EntityInsertable<TProxy, TEntity> getEntityInsertable(TEntity entity) {
        return easyEntityQuery.insertable(entity);
    }

    public EntityInsertable<TProxy, TEntity> getEntityInsertable(List<TEntity> list) {
        return easyEntityQuery.insertable(list);
    }

    public ClientEntityUpdatable<TEntity> getUpdatable(TEntity entity) {
        return easyQueryClient.updatable(entity);
    }

    public ClientEntityUpdatable<TEntity> getUpdatable(List<TEntity> list) {
        return easyQueryClient.updatable(list);
    }

    public EntityUpdatable<TProxy, TEntity> getEntityUpdatable(TEntity entity) {
        return easyEntityQuery.updatable(entity);
    }

    public EntityUpdatable<TProxy, TEntity> getEntityUpdatable(List<TEntity> list) {
        return easyEntityQuery.updatable(list);
    }

    /**
     * 生成ClientExpressionDeletable
     */
    public ClientExpressionDeletable<TEntity> getDeletable() {
        return easyQueryClient.deletable(getGenericClass())
                .disableLogicDelete()
                .allowDeleteStatement(true);
    }

    /**
     * 生成ExpressionDeletable
     */
    public ExpressionDeletable<TProxy, TEntity> getEntityDeletable() {
        return easyEntityQuery.deletable(getGenericClass())
                .disableLogicDelete()
                .allowDeleteStatement(true);
    }

    /**
     * 生成ClientQueryable
     */
    public ClientQueryable<TEntity> getQueryable() {
        return easyQueryClient.queryable(getGenericClass());
    }

    /**
     * 生成ClientQueryable, By SQL
     */
    public ClientQueryable<TEntity> getQueryableBySql(String sql) {
        return easyQueryClient.queryable(sql, getGenericClass());
    }

    /**
     * 生成EntityQueryable
     */
    public EntityQueryable<TProxy, TEntity> getEntityQueryable() {
        return easyEntityQuery.queryable(getGenericClass());
    }

    /**
     * 生成EntityQueryable, By SQL
     */
    public EntityQueryable<TProxy, TEntity> getEntityQueryableBySql(String sql) {
        return easyEntityQuery.queryable(sql, getGenericClass());
    }

    /**
     * 按查询条件生成ClientQueryable
     */
    protected ClientQueryable<TEntity> getQueryableByCondition(Map<String, String> filterMap) {
        ClientQueryable<TEntity> queryable = getQueryable();

        for (var entry : filterMap.entrySet()) {
            queryable = queryWhere(queryable, entry);
        }

        return queryable;
    }

    /**
     * 应用查询条件, 默认实现
     */
    protected ClientQueryable<TEntity> queryWhere(ClientQueryable<TEntity> queryable, Map.Entry<String, String> entry) {

        queryable = queryable.where(d -> d.eq(entry.getKey(), entry.getValue()));

        return queryable;
    }

    /**
     * 应用查询排序, 默认实现
     */
    protected ClientQueryable<TEntity> queryOrderBy(ClientQueryable<TEntity> queryable) {
        return queryable;
    }

    /**
     * 过滤请求参数, 默认实现
     */
    protected Map<String, String> filterMap(Map<String, String> map, String... extraList) {
        Map<String, String> map2 = new HashMap<>();

        List<String> filterList = Arrays.stream(extraList).collect(Collectors.toList());

        for (var entry : map.entrySet()) {
            if (Objects.equals(entry.getKey(), "_")) {
                continue;
            }

            if (filterList.contains(entry.getKey())) {
                continue;
            }

            map2.put(entry.getKey(), entry.getValue());
        }

        return map2;
    }
}
